#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

#include <ctime>
#include <pwd.h>
#include <grp.h>

void printdir(const char* path,char *dir, int depth);
void print_message(struct dirent *entry,struct stat &statbuf);

int main(int argc, char *argv[])
{
    //struct stat statbuf;
    char arg[]="/root/";//路径
    printdir(arg,arg,1);
    return 0;
}

void printdir(const char* path,char *dir,int depth)
{
    DIR *dp;
    struct dirent *entry;
    struct stat statbuf;
    printf("%s:\n",path);
    printf("深度: %d\n",depth);
    if((dp=opendir(dir))==NULL)
    {
        printf("Failed to open directory!\n");
        return;
    }
    
    chdir(dir);//跳转到当前目录
    while(entry=readdir(dp))//如果当前目录还有文件，就继续读，每次读完之后指针就会跳到下一个文件
    {
        lstat(entry->d_name,&statbuf);//读取该文件的文件信息到statbuf中。
        
        if(entry->d_name[0]=='.') continue;//比如.git文件这样就会被忽略。
        
        if((statbuf.st_mode&S_IFMT)==S_IFDIR)//判断文件类型，是目录文件就递归打印。
        {
            //是文件夹，先打印文件夹信息再跳转文件夹
            if(!strcmp(entry->d_name,".")||!strcmp(entry->d_name,".."))//跳过
                continue;
            print_message(entry,statbuf);//打印文件夹信息
            printf("\n");
            char temp_path[1000];
            strcpy(temp_path,path);
            strcat(temp_path,"/");//打印当前目录
            strcat(temp_path,entry->d_name);
            printdir(temp_path,entry->d_name,depth+1);//递归打印当前文件夹，同时深度+1，采用相对路径方式。
            printf("文件路径%s:\n",path);
            printf("深度: %d\n",depth);
            printf("\n");
        }
        else print_message(entry,statbuf);//不然就打信息。entry用在打印文件上。
    }
    
    chdir("..");//返回上一层目录
    
    closedir(dp);//关闭当前目录
}
void print_message(struct dirent *entry,struct stat &statbuf)
{
    char filemode[11];
    switch (statbuf.st_mode&S_IFMT)//文件类型
    {
        case S_IFREG: filemode[0]='-'; break;  //普通文件
        case S_IFSOCK: filemode[0]='s'; break; //socket文件
        case S_IFLNK: filemode[0]='l'; break;  
        case S_IFBLK: filemode[0]='b'; break;  //块文件
        case S_IFDIR: filemode[0]='d'; break;  //目录文件
        case S_IFCHR: filemode[0]='c'; break; 
        case S_IFIFO: filemode[0]='p'; break;  
        default: break;
    }
    for(int i=1;i<=9;i++)
    {
        if(statbuf.st_mode&(1<<(9-i)))//逐位与。比较。取低9位。
            switch(i%3){
                case 0:filemode[i]='x'; break;//可执行
                case 1:filemode[i]='r'; break;//可读
                case 2:filemode[i]='w'; break;//可写
            }
        else filemode[i]='-';//0
        //rwx:421
    }
    filemode[10] = '\0';//打完文件基本信息后，打出空格
    printf("%s ",filemode);
    printf("%5ld ",statbuf.st_nlink);//连接数
    printf("%s ",getpwuid(statbuf.st_uid)->pw_name);//所有者ID
    printf("%s ",getgrgid(statbuf.st_gid)->gr_name);//所有者组ID
    printf("%9ld ",statbuf.st_size);//大小
    printf(" %s ",4+ctime(&statbuf.st_mtime));//时间
    printf("%s\n",entry->d_name);//文件名
}